#!/bin/bash
# vim: tw=100:

# This file is part of Background Music.
#
# Background Music is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 2 of the
# License, or (at your option) any later version.
#
# Background Music is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Background Music. If not, see <http://www.gnu.org/licenses/>.

#
# postinstall
#
# Copyright © 2017-2022, 2024 Kyle Neideck
# Copyright © 2023 modue sp. z o.o.
#

# Make sure we use the built-in versions of programs, for consistency. Probably not necessary
# because the installer will set $PATH in a similar way, but it doesn't hurt.
PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH

function log {
    logger "$@"
    echo "$@"
}

function log_output {
    while read line
    do
        log "$line"
    done < /dev/stdin
}

coreaudiod_plist="/System/Library/LaunchDaemons/com.apple.audio.coreaudiod.plist"
dest_volume="$3"
xpc_helper_install_path="$(bash safe_install_dir.sh -y)"

log "Installing BGMXPCHelper to $xpc_helper_install_path"

# Delete the old version first, if any, to work around a macOS bug where a codesigned Mach-O binary
# can fail verification if you overwrite the old version instead of replacing it. Apparently the
# kernel caches some codesigning info that doesn't get updated. See
# <https://forums.developer.apple.com/thread/126187>.
rm -rf "${xpc_helper_install_path}/BGMXPCHelper.xpc"
cp -Rf "BGMXPCHelper.xpc" "$xpc_helper_install_path"

# Remove the quarantine attribute from BGMXPCHelper. Since macOS 14.0, this is needed to keep
# Gatekeeper from trying to show a permission prompt when BGMXPCHelper is launched. That would
# fail because BGMXPCHelper is a daemon, and Gatekeeper would prevent it from launching.
#
# TODO: I think, ideally, we should use the new Service Management API to install BGMXPCHelper,
#       which would probably avoid this issue. See
#       <https://developer.apple.com/documentation/servicemanagement/updating_your_app_package_installer_to_use_the_new_service_management_api>.
log "Removing com.apple.quarantine attribute from BGMXPCHelper."
sudo xattr -dr com.apple.quarantine "${xpc_helper_install_path}/BGMXPCHelper.xpc" 2>&1 | log_output
ls -lah@ "${xpc_helper_install_path}/BGMXPCHelper.xpc" 2>&1 | log_output

# TODO: Fail the install and show an error message if this fails.
bash "post_install.sh" "$xpc_helper_install_path" "BGMXPCHelper.xpc/Contents/MacOS/BGMXPCHelper" "."

# TODO: Verify the installed files, their permissions, the _BGMXPCHelper user/group, etc.

# The extra or-clauses are fallback versions of the command that restarts coreaudiod. Apparently
# some of these commands don't work with older versions of launchctl, so I figure there's no
# harm in trying a bunch of different ways (which should all work).
(sudo launchctl kickstart -k system/com.apple.audio.coreaudiod &>/dev/null || \
    sudo launchctl kill SIGTERM system/com.apple.audio.coreaudiod &>/dev/null || \
    sudo launchctl kill TERM system/com.apple.audio.coreaudiod &>/dev/null || \
    sudo launchctl kill 15 system/com.apple.audio.coreaudiod &>/dev/null || \
    sudo launchctl kill -15 system/com.apple.audio.coreaudiod &>/dev/null || \
    sudo killall coreaudiod &>/dev/null || \
    (sudo launchctl unload "$coreaudiod_plist" &>/dev/null && \
        sudo launchctl load "$coreaudiod_plist" &>/dev/null)
    ) && \
    sleep 2

# Wait until coreaudiod has restarted and BGMDevice is ready to use.
retries=5
while [[ $retries -gt 0 ]]; do
    device_list="$(system_profiler SPAudioDataType)"

    # The system_profiler command above doesn't work on GitHub Actions, so also try this
    # alternative, which does. I haven't looked into the root cause, so this may be necessary for
    # other systems as well.
    input_device_list="$(./ListInputDevices)"

    # "BGMDevice" is the UID, which is a stable identifier. device_list doesn't include the UIDs, so
    # we have to check for BGMDevice's name.
    if [[ "$device_list" =~ "Background Music" ]] || \
        [[ "$input_device_list" =~ "BGMDevice" ]]; then
        log "Background Music device is installed and available."
        # Break out of the loop.
        retries=0
    else
        retries=$((retries - 1))
        if [[ $retries -gt 0 ]]; then
            log "Background Music device not found. Trying again in 3 seconds..."
            sleep 3
        else
            # TODO: We might be able to use <installation-check> to show error messages in the
            #       installer GUI instead of just logging them. See
            #       <https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html#//apple_ref/doc/uid/TP40005370-CH100-SW12>.
            log "Background Music device not found. Installation failed."
            log "Audio devices:"
            log "$device_list"
            log "Audio input devices:"
            log "$input_device_list"
            exit 1
        fi
    fi
done

# Launch BGMApp.
#
# Allow a few retries because it seems to sometimes fail intermittently in CI builds. For example,
# <https://travis-ci.org/github/kyleneideck/BackgroundMusic/jobs/764126689>. From the error
# messages, it looks like BGMApp isn't always registered with Launch Services by this point.
logged_in_user_id="$(id -u "${USER}")"
did_open_bgmapp=false
retries=5

while [[ $did_open_bgmapp != "true" ]] && [[ $retries -gt 0 ]]; do
    retries=$((retries - 1))

    # Try opening BGMApp using its bundle ID first so the installer can declare it as "relocatable".
    # That way, if the user moves BGMApp and then installs a newer version of Background Music, the
    # installer will try to find the old version of BGMApp and put the new one in the same place.
    #
    # Use launchctl to make sure we don't run BGMApp as the installer user.
    #
    # TODO: If they have multiple copies of BGMApp, this might open one of the old ones.
    log "Opening Background Music.app by bundle ID"
    if launchctl asuser "${logged_in_user_id}" \
            open -b com.bearisdriving.BGM.App; then
        did_open_bgmapp=true
    fi

    if [[ $did_open_bgmapp != "true" ]]; then
        dest_volume_no_trailing_slash="${dest_volume/%\//}"
        log "Opening ${dest_volume_no_trailing_slash}/Applications/Background Music.app"
        if launchctl asuser "${logged_in_user_id}" \
                open "${dest_volume_no_trailing_slash}/Applications/Background Music.app"; then
            did_open_bgmapp=true
        fi
    fi

    if [[ $did_open_bgmapp != "true" ]]; then
        log "Opening Background Music.app using AppleScript"
        if osascript -e 'tell application "Background Music" to activate'; then
            did_open_bgmapp=true
        fi
    fi

    if [[ $did_open_bgmapp != "true" ]]; then
        log "Failed to open Background Music.app. Will retry shortly."
        sleep 1
    fi
done

# If we couldn't open BGMApp, it probably didn't install properly, but it's not certain, so don't
# fail the install.
if [[ $did_open_bgmapp != "true" ]]; then
    log "Failed to open Background Music.app. Giving up."
fi

# The installer plays a sound when it finishes, so give BGMApp a second to launch.
sleep 1

exit 0



